using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using HslCommunication.BasicFramework;
using HslCommunication.Core;
using HslCommunication.Core.Net;

namespace HslCommunication.Profinet.LSIS
{
	/// <summary>
	/// XGB Cnet I/F module supports Serial Port. On Tcp/ip implementation
	/// </summary>
	/// <remarks>
	/// Address example likes the follow
	/// <list type="table">
	///   <listheader>
	///     <term>地址名称</term>
	///     <term>地址代号</term>
	///     <term>示例</term>
	///     <term>地址进制</term>
	///     <term>字操作</term>
	///     <term>位操作</term>
	///     <term>备注</term>
	///   </listheader>
	///   <item>
	///     <term>*</term>
	///     <term>P</term>
	///     <term>PX100,PB100,PW100,PD100,PL100</term>
	///     <term>10</term>
	///     <term>√</term>
	///     <term>√</term>
	///     <term></term>
	///   </item>
	///   <item>
	///     <term>*</term>
	///     <term>M</term>
	///     <term>MX100,MB100,MW100,MD100,ML100</term>
	///     <term>10</term>
	///     <term>√</term>
	///     <term>√</term>
	///     <term></term>
	///   </item>
	///   <item>
	///     <term>*</term>
	///     <term>L</term>
	///     <term>LX100,LB100,LW100,LD100,LL100</term>
	///     <term>10</term>
	///     <term>√</term>
	///     <term>√</term>
	///     <term></term>
	///   </item>
	///   <item>
	///     <term>*</term>
	///     <term>K</term>
	///     <term>KX100,KB100,KW100,KD100,KL100</term>
	///     <term>10</term>
	///     <term>√</term>
	///     <term>√</term>
	///     <term></term>
	///   </item>
	///   <item>
	///     <term>*</term>
	///     <term>F</term>
	///     <term>FX100,FB100,FW100,FD100,FL100</term>
	///     <term>10</term>
	///     <term>√</term>
	///     <term>√</term>
	///     <term></term>
	///   </item>
	///   <item>
	///     <term></term>
	///     <term>T</term>
	///     <term>TX100,TB100,TW100,TD100,TL100</term>
	///     <term>10</term>
	///     <term>√</term>
	///     <term>√</term>
	///     <term></term>
	///   </item>
	///   <item>
	///     <term></term>
	///     <term>C</term>
	///     <term>CX100,CB100,CW100,CD100,CL100</term>
	///     <term>10</term>
	///     <term>√</term>
	///     <term>√</term>
	///     <term></term>
	///   </item>
	///   <item>
	///     <term></term>
	///     <term>D</term>
	///     <term>DX100,DB100,DW100,DD100,DL100</term>
	///     <term>10</term>
	///     <term>√</term>
	///     <term>√</term>
	///     <term></term>
	///   </item>
	///   <item>
	///     <term></term>
	///     <term>S</term>
	///     <term>SX100,SB100,SW100,SD100,SL100</term>
	///     <term>10</term>
	///     <term>√</term>
	///     <term>√</term>
	///     <term></term>
	///   </item>
	///   <item>
	///     <term></term>
	///     <term>Q</term>
	///     <term>QX100,QB100,QW100,QD100,QL100</term>
	///     <term>10</term>
	///     <term>√</term>
	///     <term>√</term>
	///     <term></term>
	///   </item>
	///   <item>
	///     <term></term>
	///     <term>I</term>
	///     <term>IX100,IB100,IW100,ID100,IL100</term>
	///     <term>10</term>
	///     <term>√</term>
	///     <term>√</term>
	///     <term></term>
	///   </item>
	///   <item>
	///     <term></term>
	///     <term>N</term>
	///     <term>NX100,NB100,NW100,ND100,NL100</term>
	///     <term>10</term>
	///     <term>√</term>
	///     <term>√</term>
	///     <term></term>
	///   </item>
	///   <item>
	///     <term></term>
	///     <term>U</term>
	///     <term>UX100,UB100,UW100,UD100,UL100</term>
	///     <term>10</term>
	///     <term>√</term>
	///     <term>√</term>
	///     <term></term>
	///   </item>
	///   <item>
	///     <term></term>
	///     <term>Z</term>
	///     <term>ZX100,ZB100,ZW100,ZD100,ZL100</term>
	///     <term>10</term>
	///     <term>√</term>
	///     <term>√</term>
	///     <term></term>
	///   </item>
	///   <item>
	///     <term></term>
	///     <term>R</term>
	///     <term>RX100,RB100,RW100,RD100,RL100</term>
	///     <term>10</term>
	///     <term>√</term>
	///     <term>√</term>
	///     <term></term>
	///   </item>
	/// </list>
	/// </remarks>
	public class XGBCnetOverTcp : NetworkDeviceSoloBase
	{
		/// <summary>
		/// PLC Station No.
		/// </summary>
		public byte Station
		{
			get;
			set;
		} = 5;


		/// <summary>
		/// Instantiate a Default object
		/// </summary>
		public XGBCnetOverTcp()
		{
			base.WordLength = 2;
			base.ByteTransform = new RegularByteTransform();
		}

		/// <summary>
		/// Read single byte value from plc
		/// </summary>
		/// <param name="address">Start address</param>
		/// <returns>result</returns>
		public OperateResult<byte> ReadByte(string address)
		{
			return ByteTransformHelper.GetResultFromArray(Read(address, 2));
		}

		/// <summary>
		/// Write single byte value to plc
		/// </summary>
		/// <param name="address">Start address</param>
		/// <param name="value">value</param>
		/// <returns>Whether to write the successful</returns>
		public OperateResult Write(string address, byte value)
		{
			return Write(address, new byte[1]
			{
				value
			});
		}

		/// <summary>
		/// Read single byte value from plc
		/// </summary>
		/// <param name="address">Start address</param>
		/// <returns>read result</returns>
		public async Task<OperateResult<byte>> ReadByteAsync(string address)
		{
			return ByteTransformHelper.GetResultFromArray(await ReadAsync(address, 2));
		}

		/// <summary>
		/// Write single byte value to plc
		/// </summary>
		/// <param name="address">Start address</param>
		/// <param name="value">value</param>
		/// <returns>Whether to write the successful</returns>
		public async Task<OperateResult> WriteAsync(string address, byte value)
		{
			return await WriteAsync(address, new byte[1]
			{
				value
			});
		}

		/// <inheritdoc />
		public override OperateResult<bool[]> ReadBool(string address, ushort length)
		{
			OperateResult<byte[]> operateResult = BuildReadOneCommand(Station, address, length);
			if (!operateResult.IsSuccess)
			{
				return OperateResult.CreateFailedResult<bool[]>(operateResult);
			}
			OperateResult<byte[]> operateResult2 = ReadFromCoreServer(operateResult.Content);
			if (!operateResult2.IsSuccess)
			{
				return OperateResult.CreateFailedResult<bool[]>(operateResult2);
			}
			return OperateResult.CreateSuccessResult(SoftBasic.ByteToBoolArray(ExtractActualData(operateResult2.Content, isRead: true).Content, length));
		}

		/// <summary>
		/// ReadCoil, same as ReadBool
		/// </summary>
		/// <param name="address">address, for example: MX100, PX100</param>
		/// <returns>Result</returns>
		public OperateResult<bool> ReadCoil(string address)
		{
			return ReadBool(address);
		}

		/// <summary>
		/// ReadCoil, same as ReadBool
		/// </summary>
		/// <param name="address">address, for example: MX100, PX100</param>
		/// <param name="length">array length</param>
		/// <returns>result</returns>
		public OperateResult<bool[]> ReadCoil(string address, ushort length)
		{
			return ReadBool(address, length);
		}

		/// <summary>
		/// WriteCoil
		/// </summary>
		/// <param name="address">Start Address</param>
		/// <param name="value">value for write</param>
		/// <returns>whether write is success</returns>
		public OperateResult WriteCoil(string address, bool value)
		{
			return Write(address, value);
		}

		/// <inheritdoc />
		public override OperateResult Write(string address, bool value)
		{
			return Write(address, new byte[1]
			{
				(byte)(value ? 1u : 0u)
			});
		}

		/// <inheritdoc />
		public override async Task<OperateResult<bool[]>> ReadBoolAsync(string address, ushort length)
		{
			OperateResult<byte[]> command = BuildReadOneCommand(Station, address, length);
			if (!command.IsSuccess)
			{
				return OperateResult.CreateFailedResult<bool[]>(command);
			}
			OperateResult<byte[]> read = await ReadFromCoreServerAsync(command.Content);
			if (!read.IsSuccess)
			{
				return OperateResult.CreateFailedResult<bool[]>(read);
			}
			return OperateResult.CreateSuccessResult(SoftBasic.ByteToBoolArray(ExtractActualData(read.Content, isRead: true).Content, length));
		}

		/// <inheritdoc cref="M:HslCommunication.Profinet.LSIS.XGBCnetOverTcp.ReadCoil(System.String)" />
		public async Task<OperateResult<bool>> ReadCoilAsync(string address)
		{
			return await ReadBoolAsync(address);
		}

		/// <inheritdoc cref="M:HslCommunication.Profinet.LSIS.XGBCnetOverTcp.ReadCoil(System.String,System.UInt16)" />
		public async Task<OperateResult<bool[]>> ReadCoilAsync(string address, ushort length)
		{
			return await ReadBoolAsync(address, length);
		}

		/// <inheritdoc cref="M:HslCommunication.Profinet.LSIS.XGBCnetOverTcp.WriteCoil(System.String,System.Boolean)" />
		public async Task<OperateResult> WriteCoilAsync(string address, bool value)
		{
			return await WriteAsync(address, value);
		}

		/// <inheritdoc cref="M:HslCommunication.Profinet.LSIS.XGBCnetOverTcp.WriteCoil(System.String,System.Boolean)" />
		public override async Task<OperateResult> WriteAsync(string address, bool value)
		{
			return await WriteAsync(address, new byte[1]
			{
				(byte)(value ? 1u : 0u)
			});
		}

		/// <inheritdoc />
		public override OperateResult<byte[]> Read(string address, ushort length)
		{
			OperateResult<byte[]> operateResult = BuildReadCommand(Station, address, length);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			OperateResult<byte[]> operateResult2 = ReadFromCoreServer(operateResult.Content);
			if (!operateResult2.IsSuccess)
			{
				return operateResult2;
			}
			return ExtractActualData(operateResult2.Content, isRead: true);
		}

		/// <inheritdoc />
		public override OperateResult Write(string address, byte[] value)
		{
			OperateResult<byte[]> operateResult = BuildWriteCommand(Station, address, value);
			if (!operateResult.IsSuccess)
			{
				return operateResult;
			}
			OperateResult<byte[]> operateResult2 = ReadFromCoreServer(operateResult.Content);
			if (!operateResult2.IsSuccess)
			{
				return operateResult2;
			}
			return ExtractActualData(operateResult2.Content, isRead: false);
		}

		/// <inheritdoc />
		public override async Task<OperateResult<byte[]>> ReadAsync(string address, ushort length)
		{
			OperateResult<byte[]> command = BuildReadCommand(Station, address, length);
			if (!command.IsSuccess)
			{
				return command;
			}
			OperateResult<byte[]> read = await ReadFromCoreServerAsync(command.Content);
			if (!read.IsSuccess)
			{
				return read;
			}
			return ExtractActualData(read.Content, isRead: true);
		}

		/// <inheritdoc />
		public override async Task<OperateResult> WriteAsync(string address, byte[] value)
		{
			OperateResult<byte[]> command = BuildWriteCommand(Station, address, value);
			if (!command.IsSuccess)
			{
				return command;
			}
			OperateResult<byte[]> read = await ReadFromCoreServerAsync(command.Content);
			if (!read.IsSuccess)
			{
				return read;
			}
			return ExtractActualData(read.Content, isRead: false);
		}

		/// <inheritdoc />
		public override string ToString()
		{
			return $"XGBCnetOverTcp[{IpAddress}:{Port}]";
		}

		/// <summary>
		/// AnalysisAddress IX0.0.0 QX0.0.0  MW1.0  MB1.0
		/// </summary>
		/// <param name="address"></param>
		/// <param name="QI"></param>
		/// <returns></returns>
		public static int CalculateAddressStarted(string address, bool QI = false)
		{
			if (address.IndexOf('.') < 0)
			{
				return Convert.ToInt32(address);
			}
			string[] array = address.Split('.');
			if (!QI)
			{
				return Convert.ToInt32(array[0]);
			}
			return Convert.ToInt32(array[2]);
		}

		/// <summary>
		/// NumberStyles HexNumber
		/// </summary>
		/// <param name="value"></param>
		/// <returns></returns>
		private static bool IsHex(string value)
		{
			if (string.IsNullOrEmpty(value))
			{
				return false;
			}
			bool result = false;
			for (int i = 0; i < value.Length; i++)
			{
				switch (value[i])
				{
				case 'A':
				case 'B':
				case 'C':
				case 'D':
				case 'E':
				case 'F':
				case 'a':
				case 'b':
				case 'c':
				case 'd':
				case 'e':
				case 'f':
					result = true;
					break;
				}
			}
			return result;
		}

		/// <summary>
		/// AnalysisAddress
		/// </summary>
		/// <param name="address">start address</param>
		/// <returns>analysis result</returns>
		public static OperateResult<string> AnalysisAddress(string address)
		{
			StringBuilder stringBuilder = new StringBuilder();
			try
			{
				stringBuilder.Append("%");
				char[] array = new char[15]
				{
					'P',
					'M',
					'L',
					'K',
					'F',
					'T',
					'C',
					'D',
					'S',
					'Q',
					'I',
					'N',
					'U',
					'Z',
					'R'
				};
				bool flag = false;
				for (int i = 0; i < array.Length; i++)
				{
					if (array[i] != address[0])
					{
						continue;
					}
					stringBuilder.Append(array[i]);
					char c = address[1];
					char c2 = c;
					if (c2 == 'X')
					{
						stringBuilder.Append("X");
						if (address[0] == 'I' || address[0] == 'Q')
						{
							stringBuilder.Append(CalculateAddressStarted(address.Substring(2), QI: true));
						}
						else if (IsHex(address.Substring(2)))
						{
							stringBuilder.Append(address.Substring(2));
						}
						else
						{
							stringBuilder.Append(CalculateAddressStarted(address.Substring(2)));
						}
					}
					else
					{
						stringBuilder.Append("B");
						int num = 0;
						if (address[1] == 'B')
						{
							num = CalculateAddressStarted(address.Substring(2));
							stringBuilder.Append((num == 0) ? num : (num *= 2));
						}
						else if (address[1] == 'W')
						{
							num = CalculateAddressStarted(address.Substring(2));
							stringBuilder.Append((num == 0) ? num : (num *= 2));
						}
						else if (address[1] == 'D')
						{
							num = CalculateAddressStarted(address.Substring(2));
							stringBuilder.Append((num == 0) ? num : (num *= 4));
						}
						else if (address[1] == 'L')
						{
							num = CalculateAddressStarted(address.Substring(2));
							stringBuilder.Append((num == 0) ? num : (num *= 8));
						}
						else if (address[0] == 'I' || address[0] == 'Q')
						{
							stringBuilder.Append(CalculateAddressStarted(address.Substring(1), QI: true));
						}
						else if (IsHex(address.Substring(1)))
						{
							stringBuilder.Append(address.Substring(1));
						}
						else
						{
							stringBuilder.Append(CalculateAddressStarted(address.Substring(1)));
						}
					}
					flag = true;
					break;
				}
				if (!flag)
				{
					throw new Exception(StringResources.Language.NotSupportedDataType);
				}
			}
			catch (Exception ex)
			{
				return new OperateResult<string>(ex.Message);
			}
			return OperateResult.CreateSuccessResult(stringBuilder.ToString());
		}

		/// <summary>
		/// reading address  Type of ReadByte
		/// </summary>
		/// <param name="station">plc station</param>
		/// <param name="address">address, for example: M100, D100, DW100</param>
		/// <param name="length">read length</param>
		/// <returns>command bytes</returns>
		public static OperateResult<byte[]> BuildReadByteCommand(byte station, string address, ushort length)
		{
			OperateResult<string> operateResult = AnalysisAddress(address);
			if (!operateResult.IsSuccess)
			{
				return OperateResult.CreateFailedResult<byte[]>(operateResult);
			}
			List<byte> list = new List<byte>();
			list.Add(5);
			list.AddRange(SoftBasic.BuildAsciiBytesFrom(station));
			list.Add(114);
			list.Add(83);
			list.Add(66);
			list.AddRange(SoftBasic.BuildAsciiBytesFrom((byte)operateResult.Content.Length));
			list.AddRange(Encoding.ASCII.GetBytes(operateResult.Content));
			list.AddRange(SoftBasic.BuildAsciiBytesFrom((byte)length));
			list.Add(4);
			int num = 0;
			for (int i = 0; i < list.Count; i++)
			{
				num += list[i];
			}
			list.AddRange(SoftBasic.BuildAsciiBytesFrom((byte)num));
			return OperateResult.CreateSuccessResult(list.ToArray());
		}

		/// <summary>
		/// One reading address Type of ReadByte
		/// </summary>
		/// <param name="station">plc station</param>
		/// <param name="address">address, for example: MX100, PX100</param>
		/// <param name="length">read length</param>
		/// <returns></returns>
		public static OperateResult<byte[]> BuildReadOneCommand(byte station, string address, ushort length)
		{
			OperateResult<string> operateResult = AnalysisAddress(address);
			if (!operateResult.IsSuccess)
			{
				return OperateResult.CreateFailedResult<byte[]>(operateResult);
			}
			List<byte> list = new List<byte>();
			list.Add(5);
			list.AddRange(SoftBasic.BuildAsciiBytesFrom(station));
			list.Add(114);
			list.Add(83);
			list.Add(83);
			list.Add(48);
			list.Add(49);
			list.AddRange(SoftBasic.BuildAsciiBytesFrom((byte)operateResult.Content.Length));
			list.AddRange(Encoding.ASCII.GetBytes(operateResult.Content));
			list.Add(4);
			int num = 0;
			for (int i = 0; i < list.Count; i++)
			{
				num += list[i];
			}
			list.AddRange(SoftBasic.BuildAsciiBytesFrom((byte)num));
			return OperateResult.CreateSuccessResult(list.ToArray());
		}

		/// <summary>
		/// build read command. 
		/// </summary>
		/// <param name="station">station</param>
		/// <param name="address">start address</param>
		/// <param name="length">address length</param>
		/// <returns> command</returns>
		public static OperateResult<byte[]> BuildReadCommand(byte station, string address, ushort length)
		{
			OperateResult<string> dataTypeToAddress = XGBFastEnet.GetDataTypeToAddress(address);
			if (!dataTypeToAddress.IsSuccess)
			{
				return OperateResult.CreateFailedResult<byte[]>(dataTypeToAddress);
			}
			switch (dataTypeToAddress.Content)
			{
			case "Bit":
				return BuildReadOneCommand(station, address, length);
			case "Word":
			case "DWord":
			case "LWord":
			case "Continuous":
				return BuildReadByteCommand(station, address, length);
			default:
				return new OperateResult<byte[]>(StringResources.Language.NotSupportedDataType);
			}
		}

		/// <summary>
		/// write data to address  Type of ReadByte
		/// </summary>
		/// <param name="station">plc station</param>
		/// <param name="address">address, for example: M100, D100, DW100</param>
		/// <param name="value">source value</param>
		/// <returns>command bytes</returns>
		public static OperateResult<byte[]> BuildWriteByteCommand(byte station, string address, byte[] value)
		{
			OperateResult<string> operateResult = AnalysisAddress(address);
			if (!operateResult.IsSuccess)
			{
				return OperateResult.CreateFailedResult<byte[]>(operateResult);
			}
			List<byte> list = new List<byte>();
			list.Add(5);
			list.AddRange(SoftBasic.BuildAsciiBytesFrom(station));
			list.Add(119);
			list.Add(83);
			list.Add(66);
			list.AddRange(SoftBasic.BuildAsciiBytesFrom((byte)operateResult.Content.Length));
			list.AddRange(Encoding.ASCII.GetBytes(operateResult.Content));
			list.AddRange(SoftBasic.BuildAsciiBytesFrom((byte)value.Length));
			list.AddRange(SoftBasic.BytesToAsciiBytes(value));
			list.Add(4);
			int num = 0;
			for (int i = 0; i < list.Count; i++)
			{
				num += list[i];
			}
			list.AddRange(SoftBasic.BuildAsciiBytesFrom((byte)num));
			return OperateResult.CreateSuccessResult(list.ToArray());
		}

		/// <summary>
		/// write data to address  Type of One
		/// </summary>
		/// <param name="station">plc station</param>
		/// <param name="address">address, for example: M100, D100, DW100</param>
		/// <param name="value">source value</param>
		/// <returns>command bytes</returns>
		public static OperateResult<byte[]> BuildWriteOneCommand(byte station, string address, byte[] value)
		{
			OperateResult<string> operateResult = AnalysisAddress(address);
			if (!operateResult.IsSuccess)
			{
				return OperateResult.CreateFailedResult<byte[]>(operateResult);
			}
			List<byte> list = new List<byte>();
			list.Add(5);
			list.AddRange(SoftBasic.BuildAsciiBytesFrom(station));
			list.Add(119);
			list.Add(83);
			list.Add(83);
			list.Add(48);
			list.Add(49);
			list.AddRange(SoftBasic.BuildAsciiBytesFrom((byte)operateResult.Content.Length));
			list.AddRange(Encoding.ASCII.GetBytes(operateResult.Content));
			list.AddRange(SoftBasic.BytesToAsciiBytes(value));
			list.Add(4);
			int num = 0;
			for (int i = 0; i < list.Count; i++)
			{
				num += list[i];
			}
			list.AddRange(SoftBasic.BuildAsciiBytesFrom((byte)num));
			return OperateResult.CreateSuccessResult(list.ToArray());
		}

		/// <summary>
		/// write data to address  Type of ReadByte
		/// </summary>
		/// <param name="station">plc station</param>
		/// <param name="address">address, for example: M100, D100, DW100</param>
		/// <param name="value">source value</param>
		/// <returns>command bytes</returns>
		public static OperateResult<byte[]> BuildWriteCommand(byte station, string address, byte[] value)
		{
			OperateResult<string> dataTypeToAddress = XGBFastEnet.GetDataTypeToAddress(address);
			if (!dataTypeToAddress.IsSuccess)
			{
				return OperateResult.CreateFailedResult<byte[]>(dataTypeToAddress);
			}
			switch (dataTypeToAddress.Content)
			{
			case "Bit":
				return BuildWriteOneCommand(station, address, value);
			case "Word":
			case "DWord":
			case "LWord":
			case "Continuous":
				return BuildWriteByteCommand(station, address, value);
			default:
				return new OperateResult<byte[]>(StringResources.Language.NotSupportedDataType);
			}
		}

		/// <summary>
		/// Extract actual data form plc response
		/// </summary>
		/// <param name="response">response data</param>
		/// <param name="isRead">read</param>
		/// <returns>result</returns>
		public static OperateResult<byte[]> ExtractActualData(byte[] response, bool isRead)
		{
			try
			{
				if (isRead)
				{
					if (response[0] == 6)
					{
						byte[] array = new byte[response.Length - 13];
						Array.Copy(response, 10, array, 0, array.Length);
						return OperateResult.CreateSuccessResult(SoftBasic.AsciiBytesToBytes(array));
					}
					byte[] array2 = new byte[response.Length - 9];
					Array.Copy(response, 6, array2, 0, array2.Length);
					return new OperateResult<byte[]>(BitConverter.ToUInt16(SoftBasic.AsciiBytesToBytes(array2), 0), "Data:" + SoftBasic.ByteToHexString(response));
				}
				if (response[0] == 6)
				{
					return OperateResult.CreateSuccessResult(new byte[0]);
				}
				byte[] array3 = new byte[response.Length - 9];
				Array.Copy(response, 6, array3, 0, array3.Length);
				return new OperateResult<byte[]>(BitConverter.ToUInt16(SoftBasic.AsciiBytesToBytes(array3), 0), "Data:" + SoftBasic.ByteToHexString(response));
			}
			catch (Exception ex)
			{
				return new OperateResult<byte[]>(ex.Message);
			}
		}
	}
}
